/* [n_monitor]
 *********************
 * Things to remember:
 *
 * - detail is threadsafe, use caution, all changes will be seen by all threads; recommend: only use to .get data never .set
 * - global variables are global to all threads, use load & unload to initialize & free up memory
 * - use global isInit to initialize global variables requiring detail to be setup
 * - when using global isInit, the code must be mutex protected so all threads do not run the init, just one
 * - inter-thread coordination can be coordinated via the provided mutex
 */
#include <process.h>
#include <stdio.h>

#define DLL_EXPORT
#include "start.h"
#include "Database.h"
#include "Exec.h"

#ifdef _WIN32
#ifdef _DEBUG
   #include <crtdbg.h>
   #undef THIS_FILE
   static char THIS_FILE[] = __FILE__;
   #define new       new( _NORMAL_BLOCK, __FILE__, __LINE__)
   #define malloc(s) _malloc_dbg(s, _NORMAL_BLOCK, __FILE__, __LINE__)
#endif
#endif

#include <iostream>
using namespace std;

#ifdef __cplusplus
extern "C" {
#endif	/*__cplusplus*/


// default global variables within dll
ParamManager *supported = NULL;
// module-specific global variables within dll
bool isInit = false;
Database *db = NULL;


// called after the module is loaded, useful to load global variables; Returns supported actions as a ParamManager*
DECLDIR ParamManager* load(void) {
	supported = new ParamManager();
	supported->setOverwrite(false);
	supported->set("action", "monitor");

	return supported;
}


// called just before the module is unloaded, useful to unload global variables
DECLDIR void unload(void) {
	if(supported) {
		delete supported;
	}
	if(db) {
		delete db;
	}
}


void init(HANDLE mutex, ParamManager &detail) {
	MYSQL_RES *rUpdate = NULL;

	// ensure we only init once
	if(!isInit) {
		char query[DEFAULT_BUFLEN];

		db = new Database(
					detail.get("db_user"), detail.get("db_pass"),
					detail.get("db_name"),
					detail.get("db_host"), atoi(detail.get("db_port")));
		
/*		// take action db connection failed
		if(&(db->conn) == NULL) {
			return;
		}
*/
		// re-establish mysql connection if lost
		bool connect = true;
		mysql_options(&(db->conn), MYSQL_OPT_RECONNECT, &connect);

		strcpy(query, "CREATE TABLE IF NOT EXISTS n_monitor (");
		strcat(query, "id INT NOT NULL AUTO_INCREMENT PRIMARY KEY");
		strcat(query, ", data VARCHAR(256)");
		strcat(query, ", count INT");
		strcat(query, ")");

		// thread-safe mysql call
		rUpdate = db->threadsafe_query(mutex, query);
		if(rUpdate) {
			mysql_free_result(rUpdate);
		}

		// Do we need to insert groups?
		sprintf(query, "SELECT * FROM n_monitor");

		// thread-safe mysql call
		rUpdate = db->threadsafe_query(mutex, query);
		int count = mysql_num_rows(rUpdate);
		mysql_free_result(rUpdate);

		if(count == 0) {
			char values[21][27] = {
				"PATCHING_MAINT_SCHED_MON_A",
				"PATCHING_MAINT_SCHED_MON_B",
				"PATCHING_MAINT_SCHED_MON_C",
				"PATCHING_MAINT_SCHED_TUE_A",
				"PATCHING_MAINT_SCHED_TUE_B",
				"PATCHING_MAINT_SCHED_TUE_C",
				"PATCHING_MAINT_SCHED_WED_A",
				"PATCHING_MAINT_SCHED_WED_B",
				"PATCHING_MAINT_SCHED_WED_C",
				"PATCHING_MAINT_SCHED_THU_A",
				"PATCHING_MAINT_SCHED_THU_B",
				"PATCHING_MAINT_SCHED_THU_C",
				"PATCHING_MAINT_SCHED_FRI_A",
				"PATCHING_MAINT_SCHED_FRI_B",
				"PATCHING_MAINT_SCHED_FRI_C",
				"PATCHING_MAINT_SCHED_SAT_A",
				"PATCHING_MAINT_SCHED_SAT_B",
				"PATCHING_MAINT_SCHED_SAT_C",
				"PATCHING_MAINT_SCHED_SUN_A",
				"PATCHING_MAINT_SCHED_SUN_B",
				"PATCHING_MAINT_SCHED_SUN_C"
			};

			for(int index = 0; index < 21; index ++) {
				sprintf(query, "INSERT INTO n_monitor (data) VALUES ('%s')", values[index]);

				// thread-safe mysql call
				rUpdate = db->threadsafe_query(mutex, query);
				if(rUpdate) {
					mysql_free_result(rUpdate);
				}
			}
		}

		isInit = true;
	}
}


DECLDIR void start(HANDLE mutex, ParamManager &detail, char *index) {
	MYSQL_RES *rSelect = NULL;
	MYSQL_RES *rUpdate = NULL;
	MYSQL_ROW row;
	char query[DEFAULT_BUFLEN];
	char group[DEFAULT_BUFLEN];
	char cmd[DEFAULT_BUFLEN];

	// mutex protected init sequence
	WaitForSingleObject(mutex, INFINITE);	// Request ownership of mutex
	init(mutex, detail);				// Init sequence
	ReleaseMutex(mutex);					// Release mutex for other threads

/*	// take action db connection failed
	if(&(db->conn) == NULL) {
		printf("mysql_real_connect failed\n");
		// free up mysql thread-specific memory
		mysql_thread_end();
		return;
	}
*/

	// Perform database query and display info, add one to the index
	BIGNUM *tmp = BN_new();
	BN_dec2bn(&tmp, index);
	BN_add_word(tmp, 1);
	char *sindex = BN_bn2dec(tmp);
	BN_free(tmp);
	sprintf(query, "SELECT data FROM n_monitor WHERE id = %s", sindex);	// plus 1 cause blocks start w/ 0, but table starts w/ 1

	// thread-safe mysql call
	rSelect = db->threadsafe_query(mutex, query);

	if(rSelect) {
		int num_fields = mysql_num_fields(rSelect);
        while((row = mysql_fetch_row(rSelect))) { 
			for(int i = 0; i < num_fields; i++) {
				// read off the group
				sprintf(group, "%s", row[i]);

				// exec dsget and count the results
				sprintf(cmd, "dsquery group -name \"%s\" | dsget group -members", group);
				char buffer[DEFAULT_BUFLEN];
				int count = 0;
				Exec proc(cmd);
				if(proc.isOutput()) {
					while(fgets(buffer, DEFAULT_BUFLEN, proc.output)) {
						if(strlen(buffer) > 2) {
							count ++;
						}
					}
				}
				else {
					printf("proc.output is null\n");
				}
				// Perform database query and display info
				sprintf(query, "UPDATE n_monitor SET count=%d WHERE id = %s",
						count, sindex);	// plus 1 cause blocks start w/ 0, but table starts w/ 1
				printf("query is '%s'\n", query);

				// thread-safe mysql call
				rUpdate = db->threadsafe_query(mutex, query);

				// free up the result form the mysql query
				mysql_free_result(rUpdate);
			}
        }

		// free up the result form the mysql query
        mysql_free_result(rSelect);
	}
	else {
		printf("[Thread::monitor] db query failed\n");
	}

	// free up resources
	OPENSSL_free(sindex);

	// free up mysql thread-specific memory
	mysql_thread_end();
}


#ifdef __cplusplus
}
#endif	/*__cplusplus */